#pragma once
#include <cassert>
#include "Vector3.h"
namespace NBE
{
	template< typename Real > 
	struct __declspec(dllexport) vec4
	{
		Real x, y, z, w;
		explicit vec4(Real _x = 0, Real _y = 0, Real _z = 0, Real _w = 0): x(_x),y(_y),z(_z),w(_w){}
		explicit vec4(int* _array):x((Real)_array[0]),y((Real)_array[1]),z((Real)_array[2]),w((Real)_array[3]){}
		explicit vec4(float* _array):x((Real)_array[0]),y((Real)_array[1]),z((Real)_array[2]),w((Real)_array[3]){}
		explicit vec4(vec3<Real> _v, Real _w = 0):x(_v.x),y(_v.y),z(_v.z),w(_w){}
		
		const Real dis(const vec4& other) const
		{
			vec4 minus(x,y,z,w);
			minus -= other;
			return sqrt(minus.x * minus.x + minus.y * minus.y + minus.z * minus.z + minus.w * minus.w);
		}


		vec4 normalize()
		{
			float len = sqrt( x*x + y*y +z*z + w*w );
			if (len == 0)
			{
				return *this;
			}
			x /= len;
			y /= len;
			z /= len;
			w /= len;
			return *this;
		}
		Real operator[](int i)const
		{
			assert( 0 <= i && i<4);
			return (&x)[i];
		}

		Real& operator[](int i)
		{
			assert( 0 <= i && i<4);
			return (&x)[i];
		}
		vec4 operator+( const vec4& other) const
		{
			vec4 result(*this);
			result += other;
			return result;
		}

		vec4 operator-( const vec4& other) const
		{
			vec4 result(*this);
			result -= other;
			return result;
		}

		vec4 operator-(void) 
		{
			vec4 result(*this);

			result.x = -x;
			result.y = -y;
			result.z = -z;
			result.w = -w;

			return result;
		}

		vec4& operator+=( const vec4& other)
		{
			x += other.x;
			y += other.y;
			z += other.z;
			w += other.w;
			return *this;
		}

		vec4& operator-=( const vec4& other)
		{
			x -= other.x;
			y -= other.y;
			z -= other.z;
			w -= other.w;
			return *this;
		}

		vec4& operator*=( Real scalar)
		{
			x *= scalar;
			y *= scalar;
			z *= scalar;
			w *= scalar;
			return *this;
		}

		vec4 operator*( Real scalar)const
		{
			vec4 result(*this);
			result *= scalar;
			return result;
		}

		Real operator*( vec4& other)const
		{
			return x * other.x + y * other.y + z * other.z + w * other.w; // inner product
		}

		vec4 crossProduct(vec4 &other) {

			vec4 res;

			res.x = y * other.z - z * other.y;
			res.y = z * other.x - x * other.z;
			res.z = x * other.y - y * other.x;

			return res;
		}


		bool operator==( const vec4& other)
		{
			return (x == other.x && y == other.y && z == other.z && w == other.w);
		}

		bool operator!=( const vec4& other)
		{
			return (x != other.x || y != other.y || z != other.z || w != other.w);
		}

		friend vec4 operator* (const Real scalar, const vec4& other)
		{
			vec4 res;
			for (size_t i = 0; i < 4; ++i)
			{
				res[i] = scalar * other[i];			 
			}
			return res;

		}// Real * vec3

	};

	typedef vec4< float > vec4f;
}